home *** CD-ROM | disk | FTP | other *** search
- Path: dawn.mmm.com!news
- From: kjhopps@mmm.com (Kevin J Hopps)
- Newsgroups: comp.lang.c++
- Subject: Re: How to handle error in constructor
- Date: 5 Feb 1996 13:49:56 GMT
- Organization: 3M - St. Paul, MN 55144-1000 US
- Message-ID: <4f51u4$2oq@dawn.mmm.com>
- References: <DLyyIM.5EG@teslab.lab.oz.au> <4enbts$35e@tofu.alt.net>
- Reply-To: kjhopps@mmm.com
- X-Newsreader: TIN [version 1.2 PL2]
- X-Newsreader: TIN [version 1.2 PL2]
- X-Newsreader: TIN [version 1.2 PL2]
- X-Newsreader: TIN [version 1.2 PL2]
-
- Walt Howard (walth@netcom.com) wrote:
- > On Tue, 30 Jan 1996 00:54:21 GMT, andrew@teslab.lab.oz.au (Andrew
- > Phillips) wrote:
-
- > >I'm converting and enhancing a simple program in C to C++. Without going
- > >into too much detail I have a class that represents a file on disk. The
- > >contructor opens the file, but what should it do if the file cannot be
- > >opened? The C program just calls fopen() tests the return value and
- > >if there's an error it prints a message and exits.
- > >
- > >In C++ I thought to set a flag in the constructor and have a member function
- > >that tests the flag to see if the file was opened correctly. This seems
- > >rather inelegant -- I guess exceptions would be the elegant way but seem
- > >like overkill for such a simple program. I've looked in several C++ books but
- > >error handling is given scant coverage except for exception handling.
- > >
- > >Any suggestions would be greatly appreciated.
- > >
-
- > There are several approaches. The one you came up with is similar to
- > what the C++ stream library does. However, that was written before
- > exceptions were available.
-
- > Exceptions is the only way to go on this. And you shouldn't consider
-
- The only way to view statements about "the only way" is with skepticism :-)
-
- > them overkill, they really aren't. All you have to say is, for
- > example:
-
- > try
- > {
- > if ( action() == fail )
- > throw( "dang file didn't open" );
- > //Here I "throw" a char* pointer
- > }
- > catch( char* errorMessage ) // and here I catch it and deal with it
- > {
- > printf( "%s", errorMessage );
- > exit( -1 ); // or whatever
- > }
-
- It is unlikely that the catch statement would be in the constructor.
- More than likely the catch would be in a piece of code that would
- do something about the problem.
-
- > Try it a few times and you'll see how simple it is. The books make it
- > seem complicated but it isn't. The rewards are well worth it.
-
- Throwing exceptions is easy. Writing code that behaves correctly
- when exceptions are thrown is not always so easy. A recent issue
- of C++ Report discussed many of these potential problems.
-
- > Exceptions will reliably trap and clean up a bad construction and I
- > recommend using them as such, however there are some other issues
- > involved.
-
- Exceptions do not clean up anything. The "stack unwinding" that
- is done in propagating an exception from its throw point to its
- handler will call the destructors of any automatic (stack based)
- objects that have been fully constructed since entry into the
- catch's try block. Any clean up that is done must be done in
- those destructors.
-
- > From my experience, I would say that any class whose constructor has
- > ANY chance to fail, should not do the failure-possible action during
- > the constructor. Have a function called init(void) (always void
- > argument) that does the actual activation of the object.
-
- > Have the constructor do EVERYTHING necessary to prime/lock and load
- > the object, but the init( ) function actually pulls the trigger. The
- > init() function should just have (void) as an argument because all its
- > going to do is pull the trigger right?
-
- > For a file object, the construction might set the name and file access
- > mode but the init() function would actually OPEN the file.
-
- Dividing the initialization into two parts like this is a holdover
- from the days before exceptions. This approach allowed you to
- get a return status from object initialization (via the init() func)
- while the constructor provided no direct error reporting means.
-
- Doing things in this way means that when implementing the class,
- you need to keep track of whether init was called. If one follows
- the convention that the constructor throws an exception if it cannot
- complete successfully, then the other member functions do not need
- to verify that initialization was performed. (Otherwise there would
- be no object on which to call the member function.)
-
- Using the construct/init separation also makes it more difficult
- to use the class as a member of another class. If I do this, now
- either I have to use the construct/init separation, or I have to
- call the init functions of all my member objects that use the
- construct/init mechanism.
-
- Things get even worse with multiple inheritance. And with virtual
- inheritance you have to be careful that the init() functions of
- common base classes are not called more than once.
-
- > This has many benefits. For example, if you're object's constructor
- > can fail, GLOBAL objects (those constructed at startup time) can fail
- > and you can't trap that with an try/catch exception handler. Way
- > bummer.
-
- This is indeed a problem. Global objects and exception-throwing
- constructors are not a good mix. However, global objects sometimes are
- a problem because of order-of-initialization dependencies, and might be
- avoided for a couple of reasons. One alternative to global objects is
- to provide global or public static functions that construct if necessary
- and return a reference. This provides a way to catch exceptions and
- avoid order-of-initialization problems.
- --
- Kevin J. Hopps e-mail: kjhopps@mmm.com
- 3M Company phone: (612) 737-4643
- 3M Center, Bldg. 235-2D-57 fax: (612) 737-2700
- St. Paul, MN 55144-1000 Opinions are my own. I don't speak for 3M.
- But 3M speaks for me -- I did not write the following line:
-
- Opinions expressed herein are my own and may not represent those of 3M.
-